#include <poppler-qt4.h>
#include "net_batkin_poppler_document_Document.h"
#include "net_batkin_poppler_page_Page.h"
#include "jni_helpers.h"

#include <QtCore>
#include <QtGui>
#include <qtjambi_core.h>

/*
 * Class:     net_batkin_poppler_document_Document
 * Method:    availableRenderBackends
 * Signature: ()Ljava/util/Set;
 */
JNIEXPORT jobject JNICALL Java_net_batkin_poppler_document_Document_availableRenderBackends
  (JNIEnv *env, jclass /* clazz */)
{
	QSet<Poppler::Document::RenderBackend> backends = Poppler::Document::availableRenderBackends();
	jclass set_class = env->FindClass("java/util/HashSet");
	jmethodID set_constructor = env->GetMethodID(set_class, "<init>", "()V");
	jobject backends_set = env->NewObject(set_class,set_constructor);
	jmethodID set_add = env->GetMethodID(set_class, "add", "(Ljava/lang/Object;)Z");
	
	jclass rb_class = env->FindClass("net/batkin/poppler/document/RenderBackend");
	jmethodID rb_find = env->GetStaticMethodID(rb_class, "fromNative", "(I)Lnet/batkin/poppler/document/RenderBackend;");

	QSetIterator<Poppler::Document::RenderBackend> i(backends);
	while(i.hasNext()) {
		Poppler::Document::RenderBackend be = i.next();
		jobject obj = env->CallStaticObjectMethod(rb_class,rb_find,be);
		if( obj ) {
			env->CallBooleanMethod(backends_set,set_add,obj);
		}
	}
	return backends_set;
}

/*
 * Class:     net_batkin_poppler_document_Document
 * Method:    load
 * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lnet/batkin/poppler/document/Document;
 */
JNIEXPORT jobject JNICALL Java_net_batkin_poppler_document_Document_load
  (JNIEnv *env, jclass clazz, jstring filename, jstring owner_password, jstring user_password)
{
	const char* chars = env->GetStringUTFChars(filename,NULL);
	QString qfilename = QString(chars);
	env->ReleaseStringUTFChars(filename,chars);
	QByteArray op;
	QByteArray up;
	if( owner_password != NULL ) {
		op = string_to_qbytes(env,owner_password);
	}
	if( user_password != NULL ) {
		up = string_to_qbytes(env,user_password);
	}
	Poppler::Document *doc = Poppler::Document::load(qfilename,op,up);
	if( doc == NULL ) {
		return NULL;
	}
	jmethodID constructor = env->GetMethodID(clazz,"<init>", "(J)V");
	return env->NewObject(clazz,constructor,(long)doc);
}

/*
 * Class:     net_batkin_poppler_document_Document
 * Method:    loadFromData
 * Signature: ([BLjava/lang/String;Ljava/lang/String;)Lnet/batkin/poppler/document/Document;
 */
JNIEXPORT jobject JNICALL Java_net_batkin_poppler_document_Document_loadFromData
  (JNIEnv *env, jclass clazz, jbyteArray file_data, jstring owner_password, jstring user_password)
{
	QByteArray bytes = jbytes_to_qbytes(env,file_data);
	QByteArray op;
	QByteArray up;
	if( owner_password != NULL ) {
		op = string_to_qbytes(env,owner_password);
	}
	if( user_password != NULL ) {
		up = string_to_qbytes(env,user_password);
	}
	Poppler::Document *doc = Poppler::Document::loadFromData(bytes,op,up);
	if( doc == NULL ) {
		return NULL;
	}
	jmethodID constructor = env->GetMethodID(clazz,"<init>", "(J)V");
	return env->NewObject(clazz,constructor,(long)doc);
}

/*
 * Class:     net_batkin_poppler_document_Document
 * Method:    setRenderBackend
 * Signature: (Lnet/batkin/poppler/document/RenderBackend;)V
 */
JNIEXPORT void JNICALL Java_net_batkin_poppler_document_Document_setRenderBackend
  (JNIEnv *env, jobject obj, jobject backend)
{
	int b = find_enum_ordinal(env,backend);
	Poppler::Document *doc = get_document(env,obj);
	doc->setRenderBackend((Poppler::Document::RenderBackend)b);
}

/*
 * Class:     net_batkin_poppler_document_Document
 * Method:    setRenderHint
 * Signature: (Lnet/batkin/poppler/document/RenderHint;Z)V
 */
JNIEXPORT void JNICALL Java_net_batkin_poppler_document_Document_setRenderHint
  (JNIEnv *env, jobject obj, jobject hint, jboolean on)
{
	int h = find_enum_ordinal(env,hint);
	Poppler::Document *doc = get_document(env,obj);
	Poppler::Document::RenderHint realHint = (Poppler::Document::RenderHint)(h + 1);
	doc->setRenderHint(realHint,on);
}

/*
 * Class:     net_batkin_poppler_document_Document
 * Method:    page
 * Signature: (Ljava/lang/String;)Lnet/batkin/poppler/page/Page;
 */
JNIEXPORT jobject JNICALL Java_net_batkin_poppler_document_Document_page__Ljava_lang_String_2
  (JNIEnv *env, jobject obj, jstring label)
{
	Poppler::Document *doc = get_document(env,obj);
	QString qs = qtjambi_to_qstring(env,label);
	Poppler::Page *page = doc->page(qs);
	if( page == NULL ) {
		return NULL;
	}
	jclass page_class = env->FindClass("net/batkin/poppler/page/Page");
	jmethodID constructor = env->GetMethodID(page_class,"<init>", "(Lnet/batkin/poppler/document/Document;J)V");
	return env->NewObject(page_class,constructor,obj,(long)page);
}

/*
 * Class:     net_batkin_poppler_document_Document
 * Method:    page
 * Signature: (I)Lnet/batkin/poppler/page/Page;
 */
JNIEXPORT jobject JNICALL Java_net_batkin_poppler_document_Document_page__I
  (JNIEnv *env, jobject obj, jint page_no)
{
	Poppler::Document *doc = get_document(env,obj);
	Poppler::Page *page = doc->page(page_no);
	if( page == NULL ) {
		return NULL;
	}
	jclass page_class = env->FindClass("net/batkin/poppler/page/Page");
	jmethodID constructor = env->GetMethodID(page_class,"<init>", "(Lnet/batkin/poppler/document/Document;J)V");
	return env->NewObject(page_class,constructor,obj,(long)page);
}

/*
 * Class:     net_batkin_poppler_document_Document
 * Method:    numPages
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_net_batkin_poppler_document_Document_numPages
  (JNIEnv *env, jobject obj)
{
	return get_document(env,obj)->numPages();
}

/*
 * Class:     net_batkin_poppler_document_Document
 * Method:    isLocked
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_net_batkin_poppler_document_Document_isLocked
  (JNIEnv *env, jobject obj)
{
	return get_document(env,obj)->isLocked();
}

/*
 * Class:     net_batkin_poppler_document_Document
 * Method:    cleanupDocument
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_net_batkin_poppler_document_Document_cleanupDocument
  (JNIEnv *env, jobject obj)
{
	Poppler::Document *doc = get_document(env,obj);
	delete doc;
}

/*
 * Class:     net_batkin_poppler_page_Page
 * Method:    renderToImage
 * Signature: (DDIIIILnet/batkin/poppler/page/Rotation;)Lcom/trolltech/qt/gui/QImage;
 */
JNIEXPORT jobject JNICALL Java_net_batkin_poppler_page_Page_renderToImage
  (JNIEnv *env, jobject obj, jdouble xres, jdouble yres, jint x, jint y, jint w, jint h, jobject rotate)
{
	Poppler::Page *page = get_page(env,obj);
	jint r = find_enum_ordinal(env,rotate);
	QImage image = page->renderToImage(xres,yres,x,y,w,h,(Poppler::Page::Rotation)r);
	return qtjambi_from_object(env,&image,"QImage","com/trolltech/qt/gui/",true);
}

/*
 * Class:     net_batkin_poppler_page_Page
 * Method:    label
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_net_batkin_poppler_page_Page_label
  (JNIEnv *env, jobject obj)
{
	Poppler::Page *page = get_page(env,obj);
	QString label = page->label();
	return qtjambi_from_qstring(env,label);
}

/*
 * Class:     net_batkin_poppler_page_Page
 * Method:    cleanupPage
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_net_batkin_poppler_page_Page_cleanupPage
  (JNIEnv *env, jobject obj)
{
	Poppler::Page *page = get_page(env,obj);
	delete page;
}

Poppler::Document *get_document(JNIEnv *env, jobject obj)
{
	jclass clazz = env->GetObjectClass(obj);
	jfieldID id_handle = env->GetFieldID(clazz,"_documentHandle","J");
	jlong handle = env->GetLongField(obj,id_handle);
	return (Poppler::Document*)handle;
}

Poppler::Page *get_page(JNIEnv *env, jobject obj)
{
	jclass clazz = env->GetObjectClass(obj);
	jfieldID id_handle = env->GetFieldID(clazz,"_pageHandle","J");
	jlong handle = env->GetLongField(obj,id_handle);
	return (Poppler::Page*)handle;
}

QByteArray string_to_qbytes(JNIEnv *env, jstring s)
{
	QString qs = qtjambi_to_qstring(env,s);
	return qs.toUtf8();
}

QByteArray jbytes_to_qbytes(JNIEnv *env, jbyteArray a)
{
	jsize len = env->GetArrayLength(a);
	jbyte* bytes = env->GetByteArrayElements(a,NULL);
	QByteArray qa((char*)bytes,len);
	env->ReleaseByteArrayElements(a,bytes,JNI_ABORT);
	return qa;
}

int find_enum_ordinal(JNIEnv *env, jobject obj)
{
	jclass clazz = env->GetObjectClass(obj);
	jmethodID ordinal_method = env->GetMethodID(clazz,"ordinal","()I");
	return env->CallIntMethod(obj,ordinal_method);
}


